import {
  ActionType,
  EditableProTable,
  ProColumns,
  ProForm,
  ProProvider,
} from '@ant-design/pro-components';
import { ConfigProvider, Form, FormItemProps, InputNumber, theme } from 'antd';
import { isString } from 'antd/es/button';
import { get, isFunction, pick, round, snakeCase, values } from 'lodash-es';
import { ReactNode, useEffect, useRef, useState } from 'react';
import { FormEmpty } from '../CustomForm';
import './index.less';
import { Block, BlockDivider } from '../modules/PlankInfo';
import wait from 'wait';
import { useDebounceEffect } from 'ahooks';

type RowId = string;
export const CustomEditableTable = ({
  title,
  name,
  columns,
  rowKey,
  editableRowId,
  onEditable,
  dataAddRender,
}: {
  title?: ReactNode;
  name: FormItemProps['name'];
  columns: ProColumns[];
  rowKey: RowId;
  editableRowId?: RowId;
  onEditable: (id: string) => void;
  dataAddRender?(params: { addRow(row: any): void }): ReactNode;
}) => {
  const {
    token: { colorPrimaryBg: tableRowHighLightBg },
  } = theme.useToken();

  const dataSource: any[] = ProForm.useWatch(name) ?? [];
  const editableKeys = dataSource.map((item) => {
    return item[rowKey];
  });

  const getId = (row: any) => {
    return row[rowKey];
  };
  const getCellKey = ({
    row,
    col,
  }: {
    row: any;
    col: ProColumns;
  }): string | undefined => {
    const { dataIndex } = col;
    if (!dataIndex) return;
    return getId(row) + dataIndex;
  };
  const [editableCell, setEditableCell] = useState<string>();
  const errorInputRef = useRef<HTMLInputElement>();
  const newColumns = columns.map<ProColumns>((col) => {
    if (!col.dataIndex || col.readonly || col.editable === false) {
      return col;
    }
    return {
      ...col,
      render(dom, entity, index, action, schema) {
        const { dataIndex } = schema;
        const value = dataIndex && get(entity, dataIndex);
        if (
          isFunction(col.editable) &&
          col.editable(value, entity, index) === false
        ) {
          return <div className="ediable-cell-disabled">{dom}</div>;
        }
        // console.log(schema.key, schema.dataIndex, schema.isEditable, schema);
        return (
          <div
            className="editable-cell-value-wrap"
            onClick={() => {
              // console.log('edit');
              if (errorInputRef.current) return;
              action?.startEditable?.(getId(entity));
              setEditableCell(getCellKey({ row: entity, col }));
            }}
          >
            {dom}
          </div>
        );
      },
      fieldProps(form, config) {
        // console.log(config);
        return {
          autoFocus: true,
          allowClear: false,
          async onBlur(e) {
            await wait();
            const hasError = await form
              .validateFields()
              .then(() => false)
              .catch((error) => {
                return error.errorFields.length > 0;
              });
            if (hasError) {
              errorInputRef.current = e.target;
              form.resetFields();
            }
            errorInputRef.current = undefined;
            setEditableCell(undefined);
          },
        };
      },
      editable(value, row, index) {
        // console.log(value, row, index);
        const cellKey = getCellKey({ row, col });
        if (!cellKey) return false;
        if (cellKey === editableCell) {
          // console.log(1);
          return true;
        }
        return false;
      },
    };
  });
  newColumns.forEach((col) => {
    col.width = 60;
    if (col.valueType === 'digit') {
      col.width = 80;
    }
  });
  const operationColumn = newColumns.at(-1)!;
  operationColumn.fixed = 'right';
  // operationColumn.width = 100;

  const [addDataForm] = Form.useForm();
  addDataForm.setFieldsValue(ProForm.useFormInstance().getFieldsValue(true));
  const actionRef = useRef<ActionType>();
  const addRow = async (row: any) => {
    const action = actionRef.current;
    if (!action) {
      return;
    }
    try {
      const result = await addDataForm.validateFields();
      // console.log({ result });
    } catch (error: any) {
      // console.log({ error });
      type FieldError = ReturnType<
        Awaited<typeof addDataForm.getFieldsError>
      >[number];
      let keyword = (name as string[]).at(-1)!;
      if (keyword.endsWith('s')) {
        keyword = keyword.slice(0, -1);
      }
      const hasError = (error.errorFields as FieldError[]).some(({ name }) => {
        return name.some(
          (item) =>
            isString(item) && snakeCase(item).includes(snakeCase(keyword)),
        );
      });
      if (hasError) return;
    }
    action.addEditRecord({
      ...row,
    });
    const id = get(row, rowKey);
    onEditable(id);
    setTimeout(() => {
      scrollToRow(id);
    }, 1000);
  };
  const selected = useRef<string>();
  useEffect(() => {
    selected.current = undefined;
  }, [dataSource]);
  const editableRowIdRef = useRef<string>();
  const _onEditable = onEditable;
  onEditable = (id: string) => {
    editableRowIdRef.current = id;
    _onEditable(id);
  };
  useDebounceEffect(() => {
    if (editableRowId && !(editableRowIdRef.current === editableRowId)) {
      scrollToRow(editableRowId);
    }
  }, [editableRowId]);
  const tableRender = ({ hidden }: { hidden?: boolean } = {}) => (
    <ProProvider.Provider
      // @ts-expect-error
      value={{
        valueTypeMap: {
          digit: {
            render: (text) => <>{round(Number(text), 2)}</>,
            renderFormItem: (text, props) => (
              <InputNumber
                min={0}
                formatter={(value) => round(Number(value), 2)}
                {...props?.fieldProps}
              />
            ),
          },
        },
      }}
    >
      <ConfigProvider
        theme={{ components: { InputNumber: { controlWidth: 60 } } }}
      >
        <EditableProTable
          size="small"
          bordered
          scroll={{ x: 300, y: 480 }}
          style={{ display: hidden ? 'none' : undefined }}
          actionRef={actionRef}
          name={name}
          rowKey={rowKey}
          columns={newColumns}
          editable={{
            type: 'multiple',
            editableKeys,
            actionRender: (row, config, defaultDom) => {
              return [defaultDom.delete];
            },
          }}
          recordCreatorProps={false}
          onRow={(record) => {
            const currentId = record[rowKey];
            return {
              style:
                currentId === editableRowId
                  ? { background: tableRowHighLightBg }
                  : undefined,
              onPointerOver: () => {
                onEditable(currentId);
              },
              onPointerLeave: () => {
                if (selected.current) {
                  onEditable(selected.current);
                } else {
                  onEditable('');
                }
              },
              onClick: () => {
                selected.current = currentId;
                onEditable(currentId);
              },
            };
          }}
        />
      </ConfigProvider>
    </ProProvider.Provider>
  );

  return (
    <>
      <Block>
        {title}
        <FormEmpty
          name={name}
          emptyRender={(node) => {
            return (
              <>
                {tableRender({ hidden: true })}
                {node}
              </>
            );
          }}
          noEmptyRender={() => tableRender()}
        />
      </Block>
      <BlockDivider />
      <Block>
        <Form
          form={addDataForm}
          labelCol={{ span: 4 }}
          labelAlign="left"
          colon={false}
        >
          {dataAddRender?.({ addRow })}
        </Form>
      </Block>
    </>
  );
};

function scrollToRow(key: string) {
  const row = document.querySelector(`[data-row-key="${key}"]`);
  row?.scrollIntoView({ block: 'nearest' });
}
